﻿using System;
using System.Collections.Generic;
using System.IO;
using Octopus.Logging.Impl.Text.Common.IO;

namespace Octopus.Logging.Impl.Text
{
    public class TextWriterAppender : AppenderSkeleton
    {
        #region 构造器
        /// <summary>
        /// Initializes a new instance of the <see cref="TextWriterAppender" /> class.
        /// </summary>
        public TextWriterAppender()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TextWriterAppender" /> class and
        /// sets the output destination to a new <see cref="StreamWriter"/> initialized 
        /// with the specified <see cref="Stream"/>.
        /// </summary>
        /// <param name="layout">The layout to use with this appender.</param>
        /// <param name="os">The <see cref="Stream"/> to output to.</param>
        //public TextWriterAppender(Stream os)
        //    : this(new StreamWriter(os))
        //{
        //}

        /// <summary>
        /// Initializes a new instance of the <see cref="TextWriterAppender" /> class and sets
        /// the output destination to the specified <see cref="StreamWriter" />.
        /// </summary>
        /// <param name="layout">The layout to use with this appender</param>
        /// <param name="writer">The <see cref="TextWriter" /> to output to</param>
        /// <remarks>
        /// The <see cref="TextWriter" /> must have been previously opened.
        /// </remarks>
        //public TextWriterAppender(TextWriter writer)
        //{
        //    Writer = writer;
        //}

        #endregion

        #region 公开属性

        /// <summary>
        /// Gets or set whether the appender will flush at the end 
        /// of each append operation.
        /// </summary>
        /// <value>
        /// <para>
        /// The default behaviour is to flush at the end of each 
        /// append operation.
        /// </para>
        /// <para>
        /// If this option is set to <c>false</c>, then the underlying 
        /// stream can defer persisting the logging event to a later 
        /// time.
        /// </para>
        /// </value>
        /// <remarks>
        /// Avoiding the flush operation at the end of each append results in
        /// a performance gain of 10 to 20 percent. However, there is safety
        /// trade-off involved in skipping flushing. Indeed, when flushing is
        /// skipped, then it is likely that the last few log events will not
        /// be recorded on disk when the application exits. This is a high
        /// price to pay even for a 20% performance gain.
        /// </remarks>
        public bool ImmediateFlush
        {
            get { return m_immediateFlush; }
            set { m_immediateFlush = value; }
        }

        /// <summary>
        /// Sets the <see cref="TextWriter"/> where the log output will go.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The specified <see cref="TextWriter"/> must be open and writable.
        /// </para>
        /// <para>
        /// The <see cref="TextWriter"/> will be closed when the appender 
        /// instance is closed.
        /// </para>
        /// <para>
        /// <b>Note:</b> Logging to an unopened <see cref="TextWriter"/> will fail.
        /// </para>
        /// </remarks>
        //virtual public TextWriter Writer
        //{
        //    set
        //    {
        //        lock (this)
        //        {
        //            Reset();
        //            m_qtw = new QuietTextWriter(value, FlushTimeSpan);
        //        }
        //    }
        //}


        int _FlushTimeSpan;
        public int FlushTimeSpan
        {
            get { return _FlushTimeSpan; }
            set { _FlushTimeSpan = value; }
        }
        #endregion Public Instance Properties

        #region 重写 AppenderSkeleton
        /// <summary>
        /// This method determines if there is a sense in attempting to append.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method checked if an output target has been set and if a
        /// layout has been set. 
        /// </para>
        /// </remarks>
        /// <returns><c>false</c> if any of the preconditions fail.</returns>
        override protected bool PreAppendCheck()
        {
            if (!base.PreAppendCheck())
            {
                return false;
            }

            if (m_qtw == null)
            {
                return false;
            }
            if (m_qtw.Closed)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// This method is called by the <see cref="AppenderSkeleton.DoAppend"/>
        /// method. 
        /// </summary>
        /// <param name="loggingEvent">The event to log.</param>
        /// <remarks>
        /// <para>
        /// Writes a log statement to the output stream if the output stream exists 
        /// and is writable.  
        /// </para>
        /// <para>
        /// The format of the output will depend on the appender's layout.
        /// </para>
        /// </remarks>
        override protected void Append(string content)
        {
            if (m_qtw == null) { return; }

            m_qtw.Write(content);

            if (m_immediateFlush)
            {
                m_qtw.Flush();
            }

        }

        /// <summary>
        /// Close this appender instance. The underlying stream or writer is also closed.
        /// </summary>
        /// <remarks>
        /// Closed appenders cannot be reused.
        /// </remarks>
        override public void OnClose()
        {
            lock (this)
            {
                Reset();
            }
        }

        #endregion Override implementation of AppenderSkeleton

        #region 内部实现

        /// <summary>
        /// Writes the footer and closes the underlying <see cref="TextWriter"/>.
        /// </summary>
        virtual protected void CloseWriter()
        {
            if (m_qtw != null)
            {
                try
                {
                    m_qtw.Close();
                }
                catch (Exception e)
                {
                    // do need to invoke an error handler
                    // at this late stage
                }
            }
        }


        /// <summary>
        /// Clears internal references to the underlying <see cref="TextWriter" /> 
        /// and other variables.
        /// </summary>
        /// <remarks>
        /// Subclasses can override this method for an alternate closing behaviour.
        /// </remarks>
        virtual protected void Reset()
        {
            CloseWriter();
            m_qtw = null;
        }

        #endregion Protected Instance Methods

        #region 内部字段

        /// <summary>
        /// This is the <see cref="log4net.helpers.QuietTextWriter"/> where logging events
        /// will be written to. 
        /// </summary>
        protected QuietTextWriter m_qtw;

        #endregion Protected Instance Fields

        #region 私有字段
        /// <summary>
        /// Immediate flush means that the underlying <see cref="TextWriter" /> 
        /// or output stream will be flushed at the end of each append operation.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Immediate flush is slower but ensures that each append request is 
        /// actually written. If <see cref="ImmediateFlush"/> is set to
        /// <c>false</c>, then there is a good chance that the last few
        /// logging events are not actually persisted if and when the application 
        /// crashes.
        /// </para>
        /// <para>
        /// The default value is <c>true</c>.
        /// </para>
        /// </remarks>
        private bool m_immediateFlush = true;

        #endregion Private Instance Fields
    }
}
